package org.fast.framework;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.fast.framework.annotation.RequestMapping.RequestMethod;
import org.fast.framework.annotation.ResponseBody;
import org.fast.framework.bean.Handler;
import org.fast.framework.bean.Model;
import org.fast.framework.config.MvcConfig;
import org.fast.framework.container.BeanContainer;
import org.fast.framework.loader.ConfigLoader;
import org.fast.framework.helper.ControllerHelper;
import org.fast.framework.util.ReflectionUtil;
import org.fast.framework.util.StreamUtil;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * 请求转发器
 */
@WebServlet(urlPatterns = "/*", loadOnStartup = 0)
public class DispatcherServlet extends HttpServlet {

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        // 初始化相关Helper类
        BootStrap.init();
        // 获取ServletContext对象（用于注册Servlet）
        ServletContext servletContext = servletConfig.getServletContext();
        MvcConfig mvcConfig = ConfigLoader.getMvcConfig();
        // 注册处理JSP的Servlet
        ServletRegistration jspServlet = servletContext.getServletRegistration("jsp");
        jspServlet.addMapping(mvcConfig.getJspPath() + "*");
        // 注册处理静态资源的默认Servlet
        ServletRegistration defaultServlet = servletContext.getServletRegistration("default");
        defaultServlet.addMapping(mvcConfig.getAssetPath() + "*");
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取请求方式与请求路径
        String requestMethod = request.getMethod().toUpperCase();
        String requestPath = request.getPathInfo();
        // 先获取没有请求方法限制的处理器，如果获取不到再通过请求方法获取处理器
        Handler handler = ControllerHelper.getHandler(RequestMethod.DEFAULT, requestPath);
        if (handler == null) {
            if (RequestMethod.GET.getValue().equals(requestMethod)) {
                handler = ControllerHelper.getHandler(RequestMethod.GET, requestPath);
            } else if (RequestMethod.POST.getValue().equals(requestMethod)) {
                handler = ControllerHelper.getHandler(RequestMethod.POST, requestPath);
            }
        }
        if (handler != null) {
            // 获取Controller类极其Bean实例
            Class<?> controllerClass = handler.getControllerClass();
            Object controllerBean = BeanContainer.getBean(controllerClass);
            // 创建请求参数对象
            Map<String, Object> paramMap = new HashMap<>();
            Enumeration<String> paramNames = request.getParameterNames();
            while (paramNames.hasMoreElements()) {
                String paramName = paramNames.nextElement();
                String paramValue = request.getParameter(paramName);
                if (paramMap.containsKey(paramName)) {
                    paramMap.put(paramName, paramMap.get(paramName) + "," + paramValue);
                } else {
                    paramMap.put(paramName, paramValue);
                }
            }
            // 处理body中携带的参数
            String body = URLDecoder.decode(StreamUtil.getString(request.getInputStream()), "UTF-8");
            if (StringUtils.isNotBlank(body)) {
                String[] params = StringUtils.split(body, "&");
                for (String param : params) {
                    String[] array = StringUtils.split(param, "=");
                    if (array.length == 2) {
                        String paramName = array[0];
                        String paramValue = array[1];
                        if (paramMap.containsKey(paramName)) {
                            paramMap.put(paramName, paramMap.get(paramName) + "," + paramValue);
                        } else {
                            paramMap.put(paramName, paramValue);
                        }
                    }
                }
            }

            // 调用Controller方法
            Method controllerMethod = handler.getControllerMethod();
            Object[] params = this.wrapMethodParams(controllerMethod, new Model(paramMap), request, response);
            Object result = ReflectionUtil.invokeMethod(controllerBean, controllerMethod, params);

            if (result != null) {
                // 如果含有ResponseBody注解，表示返回数据
                if (controllerMethod.isAnnotationPresent(ResponseBody.class)) {
                    response.setContentType("application/json");
                    response.setCharacterEncoding("UTF-8");
                    PrintWriter writer = response.getWriter();
                    if (result instanceof String || result instanceof Short || result instanceof Integer
                            || result instanceof Long || result instanceof Float || result instanceof Double
                            || result instanceof Byte || result instanceof Character || result instanceof Boolean) {
                        writer.write(result + "");
                    } else {
                        String json = JSON.toJSONString(result);
                        writer.write(json);
                    }
                    writer.flush();
                    writer.close();
                } else { // 否则返回视图
                    if (result instanceof String) {
                        String path = result.toString();
                        if (path.startsWith("/")) {
                            path = path.substring(1);
                        }
                        request.getRequestDispatcher(ConfigLoader.getMvcConfig().getJspPath() + path + ".jsp").forward(request, response);
                    }
                }
            }
        }
    }

    private Object[] wrapMethodParams(Method method, Model model, HttpServletRequest request, HttpServletResponse response) {
        Parameter[] methodParams = method.getParameters();
        Object[] objects = new Object[methodParams.length];
        for (int i = 0; i < objects.length; i++) {
            String typeName = methodParams[i].getType().getTypeName();
            if (typeName.equals("javax.servlet.http.HttpServletRequest")) {
                objects[i] = request;
            } else if (typeName.equals("javax.servlet.http.HttpServletResponse")) {
                objects[i] = response;
            } else if (typeName.equals("javax.servlet.http.HttpSession")) {
                objects[i] = request.getSession();
            } else if (typeName.equals("org.fast.framework.bean.Model")) {
                objects[i] = model;
            } else {
                objects[i] = null;
            }
        }
        return objects;
    }
}
